import {
  ProgressMessage,
  ResponseService,
} from '../common/protocol/response-service';
import {
  DownloadProgress,
  TaskProgress,
} from './cli-protocol/cc/arduino/cli/commands/v1/common_pb';

export interface InstallResponse {
  getProgress?(): DownloadProgress | undefined;
  getTaskProgress(): TaskProgress | undefined;
}

export namespace InstallWithProgress {
  export interface Options {
    /**
     * _unknown_ progress if falsy.
     */
    readonly progressId?: string;
    readonly responseService: ResponseService;
  }

  export function createDataCallback({
    responseService,
    progressId,
  }: InstallWithProgress.Options): (response: InstallResponse) => void {
    let localFile = '';
    let localTotalSize = Number.NaN;
    return (response: InstallResponse) => {
      const download = response.getProgress
        ? response.getProgress()
        : undefined;
      const task = response.getTaskProgress();
      if (!download && !task) {
        throw new Error(
          "Implementation error. Neither 'download' nor 'task' is available."
        );
      }
      if (task && download) {
        throw new Error(
          "Implementation error. Both 'download' and 'task' are available."
        );
      }
      if (task) {
        const message = task.getName() || task.getMessage();
        if (message) {
          if (progressId) {
            responseService.reportProgress({
              progressId,
              message,
              work: { done: Number.NaN, total: Number.NaN },
            });
          }
          responseService.appendToOutput({ chunk: `${message}\n` });
        }
      } else if (download) {
        if (download.getFile() && !localFile) {
          localFile = download.getFile();
        }
        if (download.getTotalSize() > 0 && Number.isNaN(localTotalSize)) {
          localTotalSize = download.getTotalSize();
        }

        // This happens only once per file download.
        if (download.getTotalSize() && localFile) {
          responseService.appendToOutput({ chunk: `${localFile}\n` });
        }

        if (progressId && localFile) {
          let work: ProgressMessage.Work | undefined = undefined;
          if (download.getDownloaded() > 0 && !Number.isNaN(localTotalSize)) {
            work = {
              total: localTotalSize,
              done: download.getDownloaded(),
            };
          }
          responseService.reportProgress({
            progressId,
            message: `Downloading ${localFile}`,
            work,
          });
        }
        if (download.getCompleted()) {
          // Discard local state.
          if (progressId && !Number.isNaN(localTotalSize)) {
            responseService.reportProgress({
              progressId,
              message: '',
              work: { done: Number.NaN, total: Number.NaN },
            });
          }
          localFile = '';
          localTotalSize = Number.NaN;
        }
      }
    };
  }
}
